﻿using System;
using Antlr.Runtime;
using SimpleSpreadsheet.Core.Exceptions;
using SimpleSpreadsheet.Core.Expressions;
using SimpleSpreadsheet.Core.Parser;
using SimpleSpreadsheet.Expressions;
using SimpleSpreadsheet.Framework;
using SimpleSpreadsheet.Model;

namespace SimpleSpreadsheet.ViewModels
{
    /// <summary>
    /// Worksheet ViewModel
    /// </summary>
    public class WorksheetViewModel : Screen
    {
        public WorksheetViewModel(int rows, int columns)
        {
            RowsCount = rows;
            ColumnsCount = columns;
            Worksheet = new Worksheet(ColumnsCount, RowsCount);
            CurrentCell = Cells[0][0];
            RowHeaders = GetRowHeaders(RowsCount);
            ColumnHeaders = GetColumnHeaders(ColumnsCount);
        }

        public Worksheet Worksheet { get; private set; }
        public Cell CurrentCell { get; set; }
        public string Name { get; set; }
        public int RowsCount { get; private set; }
        public int ColumnsCount { get; private set; }
        public string FileName { get; set; }

        public BindableCollection<string> RowHeaders { get; private set; }
        public BindableCollection<string> ColumnHeaders { get; private set; }

        public BindableCollection<BindableCollection<Cell>> Cells
        {
            get { return Worksheet.Cells; }
            set { Worksheet.Cells = value; }
        }

        /// <summary>
        /// Gets the collection of row headers
        /// </summary>
        /// <param name="number"></param>
        /// <returns></returns>
        private BindableCollection<string> GetRowHeaders(int number)
        {
            var list = new BindableCollection<string>();
            for (int i = 1; i <= number; i++)
            {
                list.Add(i.ToString());
            }
            return list;
        }

        /// <summary>
        /// Gets the collection of column headers
        /// </summary>
        /// <param name="number"></param>
        /// <returns></returns>
        private BindableCollection<string> GetColumnHeaders(int number)
        {
            var list = new BindableCollection<string>();
            for (int i = 0; i < number; i++)
            {
                list.Add(ReferenceRangeExpression.ColumnIndexToName(i));
            }
            return list;
        }

        /// <summary>
        /// Remembers the currently selected cell
        /// </summary>
        /// <param name="s"></param>
        public void SelectCurrentCell(object s)
        {
            var cell = s as Cell;
            if (cell != null)
                CurrentCell = Cells[cell.Row][cell.Column];
        }

        /// <summary>
        /// Open blank worksheet
        /// </summary>
        public void New()
        {
            Worksheet.New();
            CurrentCell = Cells[0][0];
        }

        /// <summary>
        /// Loads the document
        /// </summary>
        /// <param name="path"></param>
        public void Load(string path)
        {
            FileName = path;
            Worksheet.Load(path);
            CurrentCell = Cells[0][0];
        }

        /// <summary>
        /// Saves the document
        /// </summary>
        public void Save()
        {
            Worksheet.Save(FileName);
        }

        /// <summary>
        /// Updates the worksheet after a cell was edited
        /// </summary>
        /// <param name="text"></param>
        /// <param name="cellRow"></param>
        /// <param name="cellColumn"></param>
        public void UpdateWorksheet(string text, int cellRow, int cellColumn)
        {
            CurrentCell = Cells[cellRow][cellColumn];

            try
            {
                if (text == null || string.IsNullOrEmpty(text) || string.IsNullOrWhiteSpace(text))
                {
                    CurrentCell.Unsubscribe();
                    CurrentCell.Expr = null;
                    CurrentCell.Text = null;
                    Worksheet.Update(CurrentCell, text, null);
                    Worksheet.Remove(CurrentCell);
                    Refresh();
                    return;
                }
                if (CurrentCell.Text != text)
                {
                    CurrentCell.BeginUpdate();

                    if (!Worksheet.ValidateExpression(text))
                        throw new ParseException("Could not parse expression");
                    
                    var lexer = new ExpressionLexer(new ANTLRStringStream(text));
                    var parser = new ExpressionParser(new CommonTokenStream(lexer));
                    var expression = new Expression(text, Worksheet.Formulas, parser);

                    if (!Worksheet.CheckWorksheetSize(expression.References))
                        throw new NotSupportedException(
                            String.Format(
                                "This cell can't be displayed on your screen: only cells from range {0}:{1} can be used. Please upgrade to PRO version, if you want to work with large documents.",
                                "A1",
                                ReferenceRangeExpression.GetCellNameByIndexes(Worksheet.MaxRows - 1,
                                                                              Worksheet.MaxColumns - 1)));

                    if (!CurrentCell.Validate())
                        throw new NotSupportedException(
                            "Initialization of the circle referenced instruction is not supported");

                    Worksheet.Update(CurrentCell, text, expression);
                }
                CurrentCell.EndUpdate();
            }
            catch (Exception)
            {
                CurrentCell.CancelUpdate();
                throw;
            }
        }
    }
}